/* svnNotifierUtils.c
 *
 * Utility functions for notifications.
 */

/*
 * Licensed to the ConArtist Development Team (CADT).
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 * The CADT licenses this file to you under the Apache License, 
 * Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>

#include "svnNotifier.h"

static FILE *logFH = NULL;
static char *logLevels[] = {
    "Unknown", "ERROR", "WARN", "INFO", "DEBUG"
};
static char *logWhere[] = {
    "Unknown", "Setup", "Running", "Handler"
};
static int logLevel = 4;

apr_status_t makeInterestedParty(struct interestedParty **theip,
                                 apr_sockaddr_t *addr,
                                 apr_pool_t *pool)
{
    struct interestedParty *ip = apr_pcalloc(pool, sizeof(*ip));
    *theip = NULL;
    if (!ip)
        return APR_ENOMEM;

    ip->pool = pool;
    ip->sa = addr;
    if (apr_generate_random_bytes(ip->uniqueId, 32) != APR_SUCCESS)
        return APR_EGENERAL;
    ip->repos = apr_table_make(pool, 0);
    if (! ip->repos)
        return APR_EGENERAL;
    *theip = ip;
    return APR_SUCCESS;
}

/* Returns 0 if not in existing list, 1 if it does */
int checkInterestedParty(struct interestedParty *existing,
                         struct interestedParty *ip,
                         struct interestedParty **oldip)
{
    struct interestedParty *i = existing;
    *oldip = NULL;
    if (!existing)
        return 0;
    for (; i; i = i->next) {
        if (apr_sockaddr_equal(i->sa, ip->sa) &&
            i->sa->port == ip->sa->port) {
            *oldip = i;
            return 1;
        }
    }
    return 0;
}

/* Checks to see if an interestedParty already lists a repo */
int checkInterestedPartyForRepo(struct interestedParty *ip, const char *repo)
{
    return (apr_table_get(ip->repos, repo) ? 1 : 0);
}

/* Taken from svnlook.c */
static svn_error_t *generate_delta_tree(svn_repos_node_t **tree,
                                        svn_repos_t *repos,
                                        svn_fs_root_t *root,
                                        svn_revnum_t base_rev,
                                        svn_boolean_t use_copy_history,
                                        apr_pool_t *pool)
{
    svn_fs_root_t *base_root = NULL;
    const svn_delta_editor_t *editor = NULL;
    void *edit_baton = NULL;
    apr_pool_t *edit_pool = svn_pool_create(pool);
    svn_fs_t *fs = svn_repos_fs(repos);

    SVN_ERR(svn_fs_revision_root(&base_root, fs, base_rev, pool));

    SVN_ERR(svn_repos_node_editor(&editor, &edit_baton, repos, base_root,
                                  root, pool, edit_pool));
    SVN_ERR(svn_repos_replay2(root, "", SVN_INVALID_REVNUM, FALSE,
                              editor, edit_baton, NULL, NULL, edit_pool));
    *tree = svn_repos_node_from_baton(edit_baton);
    svn_pool_destroy(edit_pool);
    return SVN_NO_ERROR;
}

svn_error_t *getSVNDirsChanged(svn_repos_node_t **tree, 
                               const char *repo_path, 
                               svn_revnum_t repo_revnum, 
                               apr_pool_t *pool)
{
    svn_repos_t *repo = NULL;
    svn_fs_root_t *root = NULL;
    svn_revnum_t base_rev_id;
    svn_fs_t *fs = NULL;

    SVN_ERR(svn_repos_open(&repo, repo_path, pool));
    fs = svn_repos_fs(repo);

    if (! SVN_IS_VALID_REVNUM(repo_revnum))
        SVN_ERR(svn_fs_youngest_rev(&repo_revnum, fs, pool));

    SVN_ERR(svn_fs_revision_root(&root, fs, repo_revnum, pool));
    base_rev_id = repo_revnum - 1;

    SVN_ERR(generate_delta_tree(tree, repo, root, base_rev_id, TRUE, pool));

    svn_fs_close_root(root);
    return SVN_NO_ERROR;    
}

void nLogOpen(const char *fn)
{
    logFH = fopen(fn, "a");
}

void nLogClose(void)
{
    if (logFH) {
        fclose(logFH);
        logFH = NULL;
    }
}

void nLogLevel(const char *lvl)
{
    int i;
    for (i = 0; i < sizeof(logLevels) / sizeof(char *); i++) {
        if (strcasecmp(lvl, logLevels[i]) == 0) {
            logLevel = i;
            break;
        }
    }
}

void nLog(int level, int which, apr_pool_t *pool, const char *fmt, ...)
{
    va_list args;
    FILE *f = (logFH ? logFH : stdout);
    char dtstr[APR_RFC822_DATE_LEN];
    char *buffer = NULL;
    apr_size_t bLen = 0;

    if (level > logLevel)
        return;

    apr_rfc822_date(dtstr, apr_time_now());
    fprintf(f, "%s: ", dtstr);
    fprintf(f, "%-5s: ", logLevels[level]);
    fprintf(f, "%s: ", logWhere[which]);

    va_start(args, fmt);
    buffer = apr_pvsprintf(pool, fmt, args);
    va_end(args);

    if (!buffer)
        return;
    bLen = strlen(buffer);
    if (buffer[bLen - 1] != '\n')
        fprintf(f, "%s\n", buffer);
    else
        fprintf(f, "%s", buffer);
    fflush(f);
}
